package com.example.p002_juc;

import org.junit.Test;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.locks.ReentrantLock;

public class TestFairAndUnfair {

    private final int SIZE = 10;
    private CountDownLatch startGate = new CountDownLatch(1);
    private CountDownLatch endGate = new CountDownLatch(SIZE);

    @Test
    public void testFair() {
        ReentrantLock2 lock = new ReentrantLock2(true);
        testLock(lock);

        startGate.countDown();

        try {
            endGate.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Test
    public void testUnfair() {
        ReentrantLock2 lock = new ReentrantLock2(false);
        testLock(lock);
        startGate.countDown();

        try {
            endGate.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private void testLock(ReentrantLock2 lock) {
        for (int i = 0; i < SIZE; i++) {
            new Thread(() -> {

                try {
                    startGate.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                for (int j = 0; j < 2; j++) {
                    lock.lock();
                    try {
                        System.out.println("Lock by [" + Thread.currentThread().getName() + "], Waiting by" + lock.getQueuedThreads());

                        endGate.countDown();

                    } finally {
                        lock.unlock();
                    }
                }

            }, String.valueOf(i)).start();

        }
    }

    private class ReentrantLock2 extends ReentrantLock {

        public ReentrantLock2(boolean fair) {
            super(fair);
        }

        @Override
        public Collection<Thread> getQueuedThreads() {
            Collection<Thread> queuedThreads = super.getQueuedThreads();
            ArrayList<Thread> threads = new ArrayList<>(queuedThreads);
            Collections.reverse(threads);
            return threads;
        }

    }


}
